"use strict";

var router = require("router"),
    TokenService = require("router").TokenService,
    mongo = require('./core/mongoservice'),
    ObjectId = require('mongodb').ObjectID,
    EventEmitter = require('events').EventEmitter,
    Q = require('q');

function AlertService() {
    console.log("AlertService constructor");
}
AlertService.prototype = Object.create(TokenService.prototype);
AlertService.prototype.constructor = AlertService;

AlertService.prototype.save = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var shopId = request.payload.shop.id;

        return self.mongoInsert(shopId, request.body).then(function (alert) {
            return response.send({ request: request, content: alert });
        });
    });
};

AlertService.prototype.remove = function (request, response, promise) {
    return promise.then(function (self) {
        //self.identify(request, response);
        
        var alertId = request.body.alertId;

        return self.mongoRemoveById(alertId).then(function (alert) {
            return response.send({ request: request, content: alert });
        });
    });
};

AlertService.prototype.removeUser = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var alertId = request.body.alertId;
        var userId = request.payload.user.id;

        return self.mongoRemoveUser(alertId, userId).then(function (alert) {
            return response.send({ request: request, content: alert });
        });
    });
};

AlertService.prototype.getByGps = function (request, response, promise) {
    return promise.then(function (self) {

        var lng = parseFloat(request.query.lng);
        var lat = parseFloat(request.query.lat);

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        var options = {
            emitter: emitter
        };

        if (request.query.maxDistance) options.maxDistance = parseFloat(request.query.maxDistance);
        if (request.query.minDistance) options.minDistance = parseFloat(request.query.minDistance);
        if (request.query.num) options.num = parseInt(request.query.num);
        if (request.query.nin) options.nin = request.query.nin.toString().split(",");

        return self.mongoGetByGps(lng, lat, options);
    });
};

AlertService.prototype.getByShopFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var shopId = request.payload.shop.id;

        return self.mongoGetByShopId(shopId, { skip: skip, limit: limit }).then(function (alerts) {
            return response.send({ request: request, content: alerts });
        });
    });
};

AlertService.prototype.getByShopLogin = function (request, response, promise) {
    return promise.then(function (self) {
        
        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var login = decodeURIComponent(request.params.login);

        return self.mongoGetByShopLogin(login, { skip: skip, limit: limit }).then(function (alerts) {
            return response.send({ request: request, content: alerts });
        });
    });
};

AlertService.prototype.getByShopEmployeeFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var userId = request.payload.user.id;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        return self.mongoGetByShopEmployeeId(userId, { emitter: emitter, skip: skip, limit: limit }).then(function (alerts) {
            return response.send({ request: request, content: alerts });
        });
    });
};


AlertService.prototype.getByUserNearFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var userId = request.payload.user.id;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        return self.mongoGetByUserNear(userId, { emitter: emitter, skip: skip, limit: limit });
    });
};

AlertService.prototype.getByUserSubscriberFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var userId = request.payload.user.id;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        return self.mongoGetByUserSubscriber(userId, { emitter: emitter, skip: skip, limit: limit });
    });
};

AlertService.prototype.getByUserSubscriberFromUserId = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var userId = request.params.userId;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        return self.mongoGetByUserSubscriber(userId, { emitter: emitter, skip: skip, limit: limit });
    });
};


AlertService.prototype.getByUserLikeFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var userId = request.payload.user.id;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        return self.mongoGetByUserLike(userId, { emitter: emitter, skip: skip, limit: limit });
    });
};

AlertService.prototype.getByShopLikeFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var shopId = request.payload.shop.id;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        return self.mongoGetByShopLike(shopId, { emitter: emitter, skip: skip, limit: limit });
    });
};

AlertService.prototype.getByShopNearFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var shopId = request.payload.shop.id;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        return self.mongoGetByShopNear(shopId, { emitter: emitter, skip: skip, limit: limit });
    });
};

AlertService.prototype.getByShopSubscriberFromToken = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var skip = parseInt(request.query.skip);
        var limit = parseInt(request.query.limit);

        var shopId = request.payload.shop.id;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alerts) {
            return response.send({ request: request, content: alerts });
        });

        return self.mongoGetByShopSubscriber(shopId, { emitter: emitter, skip: skip, limit: limit });
    });
};

AlertService.prototype.getById = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);
        var shopId;
        var userId = request.payload.user.id;
        if (request.payload.shop) shopId = request.payload.shop.id;

        var alertId = request.params.alertId;

        var emitter = new EventEmitter();
        emitter.on('ready', function (alert) {
            return response.send({ request: request, content: alert });
        });

        return self.mongoGetPageById(userId, shopId, alertId, { emitter: emitter });
    });
};

AlertService.prototype.getArchivedById = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);

        var userId = request.payload.user.id;
        var alertId = request.params.alertId;

        return self.mongoGetArchivedPageById(userId, alertId ).then(function(alert){
            return response.send({ request: request, content: alert });
        });
    });
};

AlertService.prototype.setAlertAsRead = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);
        var shopId;
        var userId = request.payload.user.id;
        if (request.payload.shop) shopId = request.payload.shop.id;

        var alertId = request.body.alertId;

        return self.mongoSetAlertAsRead(alertId, userId, shopId).then(function (alert) {
            return response.send({ request: request, content: alert });
        });
    });
};

AlertService.prototype.setAlertAsLike = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);
        var shopId;
        var userId = request.payload.user.id;
        if (request.payload.shop) shopId = request.payload.shop.id;

        var alertId = request.body.alertId;

        return self.mongoSetAlertAsLike(alertId, userId, shopId).then(function (alert) {
            return response.send({ request: request, content: alert });
        });
    });
};

AlertService.prototype.unsetAlertAsLike = function (request, response, promise) {
    return promise.then(function (self) {
        self.identify(request, response);
        var shopId;
        var userId = request.payload.user.id;
        if (request.payload.shop) shopId = request.payload.shop.id;

        var alertId = request.body.alertId;

        return self.mongoUnsetAlertAsLike(alertId, userId, shopId).then(function (alert) {
            return response.send({ request: request, content: alert });
        });
    });
};

/*-------------------------------------------------------*/
/*mongo daf*/
AlertService.prototype.mongoGetById = function (alertId) {
    if (!alertId) throw new Error("alertId is not defined.");
    return mongo.findOne('alerts', { _id: new ObjectId(alertId) });
};

AlertService.prototype.mongoGetPageById = function (userId, shopId, alertId, options) {
    if (!userId) throw new Error("userId is not defined.");
    //shopId can be null
    if (!alertId) throw new Error("alertId is not defined.");
    if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    alertId = new ObjectId(alertId);

    return mongo.findOne('alerts', { _id: alertId }).then(function (alert) {
        return self.mongoSetAlertAsRead(alertId, userId, shopId).then(function () {
            var alerts = self.createPage(userId, [alert], "users");
            options.emitter.emit('ready', alerts[0]);
        });
    });
};

AlertService.prototype.mongoGetArchivedPageById = function (userId, alertId) {
    if (!userId) throw new Error("userId is not defined.");
    if (!alertId) throw new Error("alertId is not defined.");
    var self = this;

    return mongo.findOne('alerts.archives', { _id: new ObjectId(alertId) }).then(function (alert) {
        var alerts = self.createPage(userId, [alert], "users");
        return alerts[0];
    });
};

AlertService.prototype.mongoGetByGps = function (lng, lat, options) {
    if (!lng) throw new Error("lng is not defined.");
    if (!lat) throw new Error("lat is not defined.");
    if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    var opts = {
        spherical: true,
        distanceMultiplier: 0.6371,
        query: { "dates.maturity": { "$gte": new Date()} }
    };

    if (options.maxDistance) opts.maxDistance = options.maxDistance / 0.6371;
    if (options.minDistance) opts.minDistance = options.minDistance;
    if (options.num) opts.num = options.num;
    if (options.nin) {
        var nin = options.nin.reduce(function (previous, current) {
            previous.push(new ObjectId(current));
            return previous;
        }, []);
        opts.query["_id"] = { "$nin": nin };
    }

    return mongo.geoNear('alerts', lng, lat, opts).then(function (doc) {
        var alerts = self.createGpsPage(doc.results);

        var pagger = {
            minDistance: 0,
            nin: []
        }

        if (alerts.length > 0) pagger.minDistance = alerts[alerts.length - 1].distanceM;

        pagger.nin = alerts.reduce(function (previous, current) {
            if (current.distanceM == pagger.minDistance) previous.push(current._id);
            return previous;
        }, []);

        var res = {
            pagger: pagger,
            alerts: alerts
        }

        options.emitter.emit('ready', res);
        return res;
    });
};

AlertService.prototype.mongoGetByShopId = function (shopId, options) {
    if (!shopId) throw new Error("shopId is not defined.");
    //if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = {"dates.creation": -1};

    shopId = new ObjectId(shopId);

    var query = {
        "shop.id": shopId,
        "dates.maturity": { "$gte": new Date() }
    };

    return mongo.find('alerts', query, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray');
    });
};

AlertService.prototype.mongoGetByShopLogin = function (login, options) {
    if (!login) throw new Error("login is not defined.");
    var self = this;

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = {"dates.creation": -1};

    var query = {
        "shop.login": login,
        "dates.maturity": { "$gte": new Date() }
    };

    return mongo.find('alerts', query, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray');
    });
};

AlertService.prototype.mongoGetByShopEmployeeId = function (userId, options) {
    if (!userId) throw new Error("userId is not defined.");
    var self = this;

    return require("./shopservice").mongoGetByEmployeeId(userId).then(function (shop) {
        return self.mongoGetByShopId(shop._id, options);
    });
};

AlertService.prototype.mongoInsert = function (shopId, newAlert) {
    if (!shopId) throw new Error("shopId is not defined.");
    if (!newAlert) throw new Error("newAlert is not defined.");
    var self = this;

    var shopId = new ObjectId(shopId); //shop.id is not a ObjectId

    //on récupère la boutique pour obtenir la liste des utilisateurs et des boutiques à proximité
    return mongo.findOne('shops', { _id: shopId }).then(function (shop) {
        if (!shop) throw new Error("No shop: " + shopId);

        newAlert.shop = {
            id: shop._id,
            login: shop.login,
            name: shop.name,
            location: {
                address: shop.location.address,
                loc: shop.location.loc,
                city: shop.location.city,
                postal_code: shop.location.postal_code,
                neighborhood: shop.location.neighborhood
            },
            picto: shop.picto,
            category: newAlert.shop.category || shop.categories[0]
        };

        if(!newAlert.shop.category) throw new Error("alert shop category is null." + shop.name);

        //users
        newAlert.users = shop.users; //user near + user subscribers
        newAlert.users.near.forEach(function (user) {
            user.read = false;
            user.like = false;
        });
        newAlert.users.subscribers.forEach(function (user) {
            user.read = false;
            user.like = false;
        });
        newAlert.users.like = [];

        //shops
        newAlert.shops = shop.shops; //shop near + shop subscribers
        newAlert.shops.near.forEach(function (shop) {
            shop.read = false;
            shop.like = false;
        });
        newAlert.shops.subscribers.forEach(function (shop) {
            shop.read = false;
            shop.like = false;
        });
        newAlert.shops.like = [];

        newAlert.dates.creation = newAlert.dates.creation || new Date();

        self.calculateMaturity(newAlert);
        return self.updateTags(newAlert, shop);
    }).then(function (newAlert) {

        if (!newAlert.type) throw new Error("Alert type is not defined.");
        if (!newAlert.articles || newAlert.articles.length == 0 || !newAlert.articles[0].content) throw new Error("No content.");
        if (!newAlert.shop.category) throw new Error("No category.");

        return mongo.insert('alerts', newAlert).then(function (alerts) {
            var alert = alerts[0];
            return require("./tagservice").mongoSaveTags(newAlert.tags).then(function () {
                return self.mongoSaveResearch(newAlert).then(function () {
                    return alert;
                });
            });
        });
    }).then(function (alert) {
        var alt = {
            id: alert._id,
            maturity: alert.dates.maturity
        }
        return mongo.update('shops', { _id: shopId }, { $push: { "alerts.alive": alt }, $inc: { "alerts.total": 1} }).then(function () {
            return require("./alertreportservice").mongoInsert(alert).then(function () {
                return alert;
            });
        });
    });
};

AlertService.prototype.mongoRemoveById = function (alertId) {
    if (!alertId) throw new Error("alertId is not defined.");
    var self = this;

    return self.mongoGetById(alertId).then(function (alert) {
        return self.mongoRemove(alert);
    });
};

AlertService.prototype.mongoRemove = function (alert) {
    if (!alert) throw new Error("alert is not defined.");
    var self = this;

    var alt = { id: new ObjectId(alert._id) };
    alert.shop.id = new ObjectId(alert.shop.id);

    return mongo.remove('alerts', { _id: alt.id }).then(function () { //move to archive
        return mongo.update('shops', { _id: alert.shop.id }, { $pull: { "alerts.alive": alt} }).then(function () {
            return mongo.insert('alerts.archives', alert).then(function () {
                return alert;
            });
        });
    });
};

AlertService.prototype.mongoRemoveList = function (alerts) {
    if (!alerts) throw new Error("alerts is not defined.");
    var self = this;

    var promises = [];
    alerts.forEach(function (item) {
        promises.push(self.mongoRemove(item));
    })
    return Q.all(promises);
};

AlertService.prototype.mongoRemoveUser = function (alertId, userId) {
    if (!alertId) throw new Error("alertId is not defined.");
    if (!userId) throw new Error("userId is not defined.");
    var self = this;

    var usr = { id: new ObjectId(userId) };
    return mongo.update('alerts', { _id: new ObjectId(alertId) }, { $pull: { "users.near": usr} }).then(function (res) {
        return mongo.update('alerts', { _id: new ObjectId(alertId) }, { $pull: { "users.subscribers": usr} }).then(function () {
            return mongo.update('alerts', { _id: new ObjectId(alertId) }, { $pull: { "users.like": usr} }).then(function () {
                return self.mongoGetById(alertId).then(function (alert) {
                    return require("./alertreportservice").mongoUpdate(alert).then(function () {
                        return res;
                    });
                });
            });
        });
    });
};

AlertService.prototype.mongoGetByUserNear = function (userId, options) {
    if (!userId) throw new Error("userId is not defined.");
    if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    var usr = { id: new ObjectId(userId) };

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = { "dates.creation": -1 };

    var query = {
        "users.near.id": usr.id,
        "dates.maturity": { "$gte": new Date() }
    };

    return mongo.find('alerts', query, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray').then(function (alerts) {
            options.emitter.emit('ready', self.createPage(userId, alerts, "users.near"));
            return alerts;
        });
    });
};

AlertService.prototype.mongoGetByUserLike = function (userId, options) {
    if (!userId) throw new Error("userId is not defined.");
    if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    var usr = { id: new ObjectId(userId) };

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = { "dates.creation": -1 };

    var query = {
        "users.like.id": usr.id,
        "dates.maturity": { "$gte": new Date() }
    };

    return mongo.find('alerts', query, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray').then(function (alerts) {
            options.emitter.emit('ready', self.createPage(userId, alerts, "users.like"));
            return alerts;
        });
    });
};

AlertService.prototype.mongoGetByShopLike = function (shopId, options) {
    if (!shopId) throw new Error("shopId is not defined.");
    if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    var shp = { id: new ObjectId(shopId) };

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = { "dates.creation": -1 };

    var query = {
        "shop.like.id": shp.id,
        "dates.maturity": { "$gte": new Date() }
    };

    return mongo.find('alerts', query, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray').then(function (alerts) {
            options.emitter.emit('ready', self.createPage(userId, alerts, "shops.like"));
            return alerts;
        });
    });
};

AlertService.prototype.mongoSetAlertAsLikeForUser = function (alertId, userId) {
    if (!alertId) throw new Error("alertId is not defined.");
    if (!userId) throw new Error("userId is not defined.");
    var self = this;


    alertId = new ObjectId(alertId);
    userId = new ObjectId(userId);
    var lke = { id: userId, like: true, read: true };

    return mongo.update('alerts', { _id: alertId }, { $push: { "users.like": lke} }).then(function () {
        return mongo.update('alerts', { _id: alertId, "users.near.id": userId }, { $set: { "users.near.$.like": true} }).then(function () {
            return mongo.update('alerts', { _id: alertId, "users.subscribers.id": userId }, { $set: { "users.subscribers.$.like": true} }).then(function () {
                return self.mongoGetById(alertId).then(function (a) {
                    return require("./alertreportservice").mongoUpdate(a);
                });
            });
        });
    });
};

AlertService.prototype.mongoSetAlertAsLikeForShop = function (alertId, shopId) {
    if (!alertId) throw new Error("alertId is not defined.");
    if (!shopId) throw new Error("shopId is not defined.");
    var self = this;

    alertId = new ObjectId(alertId);
    shopId = new ObjectId(shopId);
    var lke = { id: shopId, like: true, read: true };
    return mongo.update('alerts', { _id: alertId }, { $push: { "shops.like": lke} }).then(function () {
        return mongo.update('alerts', { _id: alertId, "shops.near.id": shopId }, { $set: { "shops.near.$.like": true} }).then(function () {
            return mongo.update('alerts', { _id: alertId, "shops.subscribers.id": shopId }, { $set: { "shops.subscribers.$.like": true} }).then(function () {
                return self.mongoGetById(alertId).then(function (a) {
                    return require("./alertreportservice").mongoUpdate(a);
                });
            });
        });
    });
};

AlertService.prototype.mongoSetAlertAsLike = function (alertId, userId, shopId) {
    if (!alertId) throw new Error("alertId is not defined.");
    if (!userId) throw new Error("userId is not defined.");
    var self = this;

    return self.mongoSetAlertAsLikeForUser(alertId, userId).then(function (data) {
        if (!shopId) return data;
        return self.mongoSetAlertAsLikeForShop(alertId, shopId).then(function (data) {
            return data;
        });
    });
};

AlertService.prototype.mongoSetAlertAsRead = function (alertId, userId, shopId) {
    if (!alertId) throw new Error("alert is not defined.");
    if (!userId) throw new Error("userId is not defined.");
    var self = this;

    return self.mongoSetAlertAsReadForUser(alertId, userId).then(function (data) {
        if (!shopId) return data;
        return self.mongoSetAlertAsReadForShop(alertId, shopId).then(function (data) {
            return data;
        });
    });
};

AlertService.prototype.mongoSetAlertAsReadForUser = function (alertId, userId) {
    if (!alertId) throw new Error("alert is not defined.");
    if (!userId) throw new Error("userId is not defined.");
    var self = this;

    alertId = new ObjectId(alertId);
    userId = new ObjectId(userId);

    return mongo.update('alerts', { _id: alertId, "users.near.id": userId }, { $set: { "users.near.$.read": true} }).then(function () {
        return mongo.update('alerts', { _id: alertId, "users.subscribers.id": userId }, { $set: { "users.subscribers.$.read": true} }).then(function () {
            return mongo.update('alerts', { _id: alertId, "users.like.id": userId }, { $set: { "users.like.$.read": true} }).then(function () {
                return self.mongoGetById(alertId).then(function (a) {
                    return require("./alertreportservice").mongoUpdate(a);
                });
            });
        });
    });
};

AlertService.prototype.mongoSetAlertAsReadForShop = function (alertId, shopId) {
    if (!alertId) throw new Error("alert is not defined.");
    if (!shopId) throw new Error("shopId is not defined.");
    var self = this;

    alertId = new ObjectId(alertId);
    shopId = new ObjectId(shopId);

    return mongo.update('alerts', { _id: alertId, "shops.near.id": shopId }, { $set: { "shops.near.$.read": true} }).then(function () {
        return mongo.update('alerts', { _id: alertId, "shops.subscribers.id": shopId }, { $set: { "shops.subscribers.$.read": true} }).then(function () {
            return self.mongoGetById(alertId).then(function (a) {
                return require("./alertreportservice").mongoUpdate(a);
            });
        });
    });
};

AlertService.prototype.mongoGetByShopNear = function (shopId, options) {
    if (!shopId) throw new Error("shopId is not defined.");
    if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    var shp = { id: new ObjectId(shopId) };

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = {"dates.creation": -1};

    var query = {
        "shops.near.id": shp.id,
        "dates.maturity": { "$gte": new Date() }
    };

    return mongo.find('alerts', query, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray').then(function (alerts) {
            options.emitter.emit('ready', self.createPage(shopId, alerts, "shops.near"));
            return alerts;
        });
    });
};


AlertService.prototype.mongoGetByUserSubscriber = function (userId, options) {
    if (!userId) throw new Error("userId is not defined.");
    if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    var usr = { id: new ObjectId(userId) };

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = { "dates.creation": -1 };

    var query = {
        "users.subscribers.id": usr.id,
        "dates.maturity": { "$gte": new Date() }
    };

    //TOTO gérer skip and take
    return mongo.find('alerts', query, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray').then(function (alerts) {
            options.emitter.emit('ready', self.createPage(userId, alerts, "users.subscribers"));
            return alerts;
        });
    });
};

AlertService.prototype.mongoGetByShopSubscriber = function (shopId, options) {
    if (!shopId) throw new Error("shopId is not defined.");
    if (!options || !options.emitter) throw new Error("options.emitter is not defined.");
    var self = this;

    var shp = { id: new ObjectId(shopId) };

    var opts = {};
    options = options || {}
    if (options.limit) opts.limit = options.limit;
    if (options.skip) opts.skip = options.skip;
    opts.sort = {"dates.creation": -1};

    var query = {
        "shops.subscribers.id": shp.id,
        "dates.maturity": { "$gte": new Date() }
    };

    //TOTO gérer skip and take
    return mongo.find('alerts', query, opts).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray').then(function (alerts) {
            options.emitter.emit('ready', self.createPage(shopId, alerts, "shops.subscribers"));
            return alerts;
        });
    });
};

AlertService.prototype.mongoArchive = function () {
    var self = this;

    var query = {
        "dates.maturity": { "$lte": new Date() }
    };

    //TOTO gérer skip and take
    return mongo.find('alerts', query).then(function (cursor) {
        return Q.ninvoke(cursor, 'toArray').then(function (alerts) {
            return self.mongoRemoveList(alerts);
        });
    });
}


/*--------------------------------------------*/
/*Common*/

AlertService.prototype.updateTags = function (alert, shop) {
    return require("./tagservice").extractHashtag(alert.content).then(function (res) {
        alert.tags = alert.tags = [];

        res.tags.forEach(function (tag) {
            if (alert.tags.indexOf(tag) == -1) alert.tags.push(tag);
        });

        if (shop.location.city) alert.tags.push(shop.location.city);
        if (shop.location.departement) alert.tags.push(shop.location.departement);
        if (shop.location.postal_code) alert.tags.push(shop.location.postal_code);
        if (shop.location.route) alert.tags.push(shop.location.route);
        if (shop.location.neighborhood) alert.tags.push(shop.location.neighborhood);

        if(!alert.shop.category) throw new Error("alert shop category is null.");
        alert.tags.push(alert.shop.category.name);

        return alert;
    });
};

AlertService.prototype.mongoSaveResearch = function (alert) {
    var research = [];
    
    return require("./researchservice").mongoSaveList(research);
};

AlertService.prototype.distanceToString = function (distanceM) {
    if (distanceM < 0) return "?";
    else if (distanceM < 100) return "100m";
    else if (distanceM < 100) return "200m";
    else if (distanceM < 500) return "500m";
    else if (distanceM < 1000) return "1km";
    else if (distanceM < 2000) return "2km";
    else if (distanceM < 5000) return "5km";
    else if (distanceM < 10000) return "10km";
    else if (distanceM < 20000) return "20km";
    else if (distanceM < 50000) return "50km";
    else return "> 50km"
}

AlertService.prototype.calculateMaturity = function (alert) {
    var deplayms = 0;
    if (alert.dates.delay == "30m") deplayms = 30 * 60 * 1000;
    else if (alert.dates.delay == "1h") deplayms = 1 * 60 * 60 * 1000;
    else if (alert.dates.delay == "3h") deplayms = 3 * 60 * 60 * 1000;
    else if (alert.dates.delay == "6h") deplayms = 6 * 60 * 60 * 1000;
    else if (alert.dates.delay == "1d") deplayms = 24 * 60 * 60 * 1000;
    else if (alert.dates.delay == "3d") deplayms = 3 * 24 * 60 * 60 * 1000;
    else if (alert.dates.delay == "10d") deplayms = 10 * 24 * 60 * 60 * 1000;
    else throw new Error("Unknown alert delay: " + alert.dates.delay);

    alert.dates.maturity = new Date(alert.dates.creation.getTime() + deplayms);
}

AlertService.prototype.getInfoFunction = function (id, type) {

    if (type == "users.near") {
        return function (current) {
            var res = current.users.near.filter(function (e) {
                return e.id.toString() == id;
            });
            return res.length > 0 ? res[0] : { distanceM: -1, read: false, like: false };
        };
    }
    else if (type == "users.subscribers") {
        return function (current) {
            var res = current.users.subscribers.filter(function (e) {
                return e.id.toString() == id;
            });
            return res.length > 0 ? res[0] : { distanceM: -1, read: false, like: false };
        };
    }
    else if (type == "users.like") {
        return function (current) {
            var res = current.users.like.filter(function (e) {
                return e.id.toString() == id;
            });
            return res.length > 0 ? res[0] : { distanceM: -1, read: false, like: false };
        };
    }
    else if (type == "shops.near") {
        return function (current) {
            var res = current.shops.near.filter(function (e) {
                return e.id.toString() == id;
            });
            return res.length > 0 ? res[0] : { distanceM: -1, read: false, like: false };
        };
    }
    else if (type == "shops.subscribers") {
        return function (current) {
            var res = current.shops.subscribers.filter(function (e) {
                return e.id.toString() == id;
            });
            return res.length > 0 ? res[0] : { distanceM: -1, read: false, like: false };
        };
    }
    else if (type == "shops.like") {
        return function (current) {
            var res = current.shops.like.filter(function (e) {
                return e.id.toString() == id;
            });
            return res.length > 0 ? res[0] : { distanceM: -1, read: false, like: false };
        };
    }
    else if (type == "users") { /*subsribers or near */
        return function (current) {
            var array = current.users.near.filter(function (e) {
                return e.id.toString() == id;
            });

            var array2 = current.users.subscribers.filter(function (e) {
                return e.id.toString() == id;
            });

            var array3 = current.users.like.filter(function (e) {
                return e.id.toString() == id;
            });

            var res = array.concat(array2.concat(array3));
            return res.length > 0 ? res[0] : { distanceM: -1, read: false, like: false };
        };
    }
    else throw new Error("Unknown type: " + type);
};



AlertService.prototype.createPage = function (id, alerts, type) {
    if (!alerts) throw new Error("alerts is not defined.");
    var self = this;
    var now = new Date();

    var fn = self.getInfoFunction(id, type);

    return alerts.reduce(function (previous, current) {
        var obj = fn(current);

        current.users = {
            subscribers: {
                total: current.users.subscribers.length
            },
            near: {
                total: current.users.near.length
            },
            like: {
                total: current.users.like.length
            }
        };
        current.shops = {
            subscribers: {
                total: current.shops.subscribers.length
            },
            near: {
                total: current.shops.near.length
            },
            like: {
                total: current.shops.like.length
            }
        };

        previous.push({
            _id: current._id,
            type: current.type,
            icon: current.icon,
            content: current.content,
            shop: current.shop,
            distance: self.distanceToString(obj.distanceM),
            distanceM: obj.distanceM,
            articles: current.articles,
            dates: current.dates,
            read: obj.read,
            like: obj.like,
            users: current.users,
            shops: current.shops,
        });

        return previous;
    }, []);
}

AlertService.prototype.createGpsPage = function (results) {
    if (!results) throw new Error("alerts is not defined.");
    var self = this;

    return results.reduce(function (previous, current) {
        current.obj.users = {
            subscribers: {
                total: current.obj.users.subscribers.length
            },
            near: {
                total: current.obj.users.near.length
            },
            like: {
                total: current.obj.users.like.length
            }
        };
        current.obj.shops = {
            subscribers: {
                total: current.obj.shops.subscribers.length
            },
            near: {
                total: current.obj.shops.near.length
            },
            like: {
                total: current.obj.shops.like.length
            }
        };

        previous.push({
            _id: current.obj._id,
            type: current.obj.type,
            icon: current.obj.icon,
            content: current.obj.content,
            shop: current.obj.shop,
            distance: self.distanceToString(current.dis),
            distanceM: current.dis,
            articles: current.obj.articles,
            dates: current.obj.dates,
            read: current.obj.read,
            like: current.obj.like,
            users: current.obj.users,
            shops: current.obj.shops,
        });

        return previous;
    }, []);
}

exports = module.exports = new AlertService();